package cn.liuyang.tinyioc.aop;

import org.aspectj.weaver.tools.PointcutExpression;
import org.aspectj.weaver.tools.PointcutParser;
import org.aspectj.weaver.tools.PointcutPrimitive;
import org.aspectj.weaver.tools.ShadowMatch;

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

/**
 * AspectJ表达式切点类
 *
 * @author liuyang
 * @date 2018/4/17.
 */
public class AspectJExpressionPointcut implements Pointcut,ClassFilter, MethodMatcher {

    private String expression;

    private PointcutParser pointcutParser;

    private PointcutExpression pointcutExpression;

    private static final Set<PointcutPrimitive> DEFAULT_SUPPORT_EXPRESSIONS = new HashSet<PointcutPrimitive>();

    static {
        // 自定义要解析的切点
        DEFAULT_SUPPORT_EXPRESSIONS.add(PointcutPrimitive.EXECUTION);
        DEFAULT_SUPPORT_EXPRESSIONS.add(PointcutPrimitive.ARGS);
        DEFAULT_SUPPORT_EXPRESSIONS.add(PointcutPrimitive.REFERENCE);
        DEFAULT_SUPPORT_EXPRESSIONS.add(PointcutPrimitive.THIS);
        DEFAULT_SUPPORT_EXPRESSIONS.add(PointcutPrimitive.TARGET);
        DEFAULT_SUPPORT_EXPRESSIONS.add(PointcutPrimitive.WITHIN);
        DEFAULT_SUPPORT_EXPRESSIONS.add(PointcutPrimitive.AT_ANNOTATION);
        DEFAULT_SUPPORT_EXPRESSIONS.add(PointcutPrimitive.AT_WITHIN);
        DEFAULT_SUPPORT_EXPRESSIONS.add(PointcutPrimitive.AT_ARGS);
        DEFAULT_SUPPORT_EXPRESSIONS.add(PointcutPrimitive.AT_TARGET);
    }

    public  AspectJExpressionPointcut() {
        this(DEFAULT_SUPPORT_EXPRESSIONS);
    }

    public AspectJExpressionPointcut(Set<PointcutPrimitive> pointcutPrimitives) {
        pointcutParser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(pointcutPrimitives);
    }

    @Override
    public MethodMatcher getMethodMatcher() {
        checkReadyToMatch();
        return this;
    }

    @Override
    public ClassFilter getClassFilter(){
        checkReadyToMatch();
        return this;
    }

    public void checkReadyToMatch() {
        if (pointcutExpression == null) {
            buildPointcutExpression();
        }
    }

    public void buildPointcutExpression() {
        pointcutExpression = pointcutParser.parsePointcutExpression(expression);
    }


    @Override
    public boolean matches(Method method, Class targetClass) {
        checkReadyToMatch();
        ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method);
        if (shadowMatch.alwaysMatches()) {
            return true;
        }
        if (shadowMatch.neverMatches()) {
            return false;
        }
        // 其他情况不判断了
        return false;
    }

    @Override
    public boolean matches(Class targetClass) {
        checkReadyToMatch();
        return pointcutExpression.couldMatchJoinPointsInType(targetClass);
    }

    public void setExpression(String expression) {
        this.expression = expression;
    }
}
